3.5 Kontrollstrukturen  
Es gibt sicherlich kein ernsthaftes Anwendungsprogramm, das ohne die Steuerung des Programmablaufs zur Laufzeit auskommt. Das Programm muss Entscheidungen treffen, die vom aktuellen Zustand oder den Benutzereingaben abhängen, es muss vielleicht Programmcode wiederholt ausführen. Jede Programmiersprache kennt daher Kontrollstrukturen, um den Programmablauf der aktuellen Situation angepasst zu steuern. In diesem Abschnitt werden Sie alle Möglichkeiten kennen lernen, die Sie unter Visual Basic nutzen können.
3.5.1 Die If-Anweisung  
Die If-Anweisung bietet sich an, wenn bestimmte Programmteile nur beim Eintreffen einer oder mehrerer Bedingungen ausgeführt werden sollen. Betrachten Sie dazu das folgende Beispiel:
| Sub Main()
|
| Dim strEingabe As String
|
| Console.Write("Geben Sie Ihren Namen ein: ")
|
| strEingabe = Console.ReadLine()
|
| If strEingabe = "" Then
|
| Console.WriteLine("Haben Sie keinen Namen?")
|
| Else
|
| Console.WriteLine("Ihr Name lautet {0}", strEingabe)
|
| End If
|
| Console.ReadLine()
|
| End Sub
|
Das Programm fordert den Anwender dazu auf, seinen Namen anzugeben. Die Benutzereingabe wird von der Methode ReadLine der Klasse Console entgegengenommen und als Rückgabewert des Aufrufs der Variablen strEingabe zugewiesen. Die Eingabe an der Konsole wird mit dem Zeilenumbruch – also dem Drücken der (Enter)-Taste – abgeschlossen. Der Zeilenumbruch gehört nicht zur zurückgelieferten Zeichenfolge.
Um sicherzustellen, dass der Anwender überhaupt eine Eingabe vorgenommen hat, die aus mindestens einem Zeichen besteht, wird der Inhalt der Stringvariablen strEingabe überprüft. Enthält sie einen Leerstring, wird an der Konsole »Haben Sie keinen Namen?« ausgegeben, ansonsten der Name mit einem entsprechenden Begleittext.
Kernkonstrukt der Überprüfung ist die If-Struktur, deren Syntax wie folgt beschrieben wird:
| If <Bedingung1> Then
|
| 'Anweisungen1
|
| [ElseIf <Bedingung2> Then
|
| 'Anweisungen2
|
| ElseIf <Bedingung3> Then
|
| 'Anweisungen3
|
| Else
|
| 'Anweisungen4]
|
| End If
|
Beim Eintritt in die Bedingungsstruktur wird zunächst die Bedingung1 geprüft. Ist deren Aussage wahr (True), werden alle folgenden Anweisungen ausgeführt, entweder bis zum nächsten Else-Zweig oder – im Fall der einfachen Bedingungsprüfung – bis zum End If. Der Programmablauf setzt sich danach mit der dem End If folgenden Codezeile fort.
Ist das Ergebnis der Bedingung1 falsch, wird die Bedingung2 in gleicher Weise getestet. Ist Bedingung2 wahr, werden die direkt folgenden Anweisungen ausgeführt, ansonsten die Bedingung3 getestet. Ist keine der Bedingungen wahr, können alle sonstigen Fälle in einem Else-Zweig behandelt werden.
Beachten Sie bitte, dass alle ElseIf- und die Else-Anweisung optional sind. In jedem Fall wird nach der Ausführung der Anweisungen der Code hinter End If fortgesetzt. Im einfachsten Fall wird also nur eine Bedingung geprüft.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 3.8 Flussdiagramm der If-Anweisung
Zur Formulierung der logischen Bedingung stehen die Vergleichsoperatoren =, <, >, >=, <= und Is zur Verfügung. Strings können außerdem noch mit Like auf ein bestimmtes Muster hin untersucht werden.
Eine Bedingung lässt sich auch aus der Verknüpfung mehrerer Teilbedingungen mit den logischen Operatoren bilden, beispielsweise:
| If x = 10 And y > 20 Then
|
| 'Anweisungen1
|
| End If
|
Die Anweisungen werden genau dann ausgeführt, wenn x = 10 ist und gleichzeitig y einen Wert größer 20 aufweist. Wollen Sie auf die Prüfung der zweiten Teilbedingung verzichten, wenn die erste bereits False ist und damit auch der gesamte Testausdruck, können Sie die Bedingungen anstelle von And auch mit dem Operator AndAlso verknüpfen.
Es gibt noch eine kürzere, einzeilige Variante der If-Struktur:
| If <Bedingung> Then <Anweisung(1)> [:<Anweisung2>, ...]
|
Wesentlichstes Merkmal ist, dass diese Form der bedingten Anweisung nicht mit dem ansonsten obligatorischen End If abgeschlossen wird. Es können sogar im Fall einer wahren Bedingung mehrere Anweisungen ausgeführt werden, die alle durch einen Doppelpunkt getrennt werden. Ferner ist auch noch die Angabe von Else möglich, allerdings keine zweite Bedingungsprüfung mit ElseIf.
| If bol Then intVar = 3 Else intVar = 10
|
Die einzeilige Variante eignet sich nur für sehr kleine Tests und ist in der Regel nicht empfehlenswert. Die Blockform ist meist vorzuziehen, weil sie durch die saubere Strukturierung besser lesbar ist und ein einfacheres Debuggen ermöglicht.
If-Anweisungen dürfen beliebig verschachtelt werden. Dazu wird ein If-Block vollständig in einen anderen eingebettet. Das folgende Beispielprogramm demonstriert diese Technik. Im Code wird mit dem Zufallszahlengenerator eine Zahl zwischen 0 und 9 ermittelt, und der Anwender soll versuchen, diese Zahl zu erraten.
| ' ----------------------------------------------------------
|
| ' Beispiel: ...\Kapitel 3\Zufallszahl
|
| ' ----------------------------------------------------------
|
| Module Module1
|
| Sub Main()
|
| Dim strText As String
|
| Dim intWahl As Int32
|
| 'Zufallszahl auf Basis der Klasse Random
|
| Dim rnd As New System.Random()
|
| Dim intZufall As Int32
|
| 'Zufallszahl im Bereich 0 <= intZufall < 10 ermitteln
|
| intZufall = rnd.Next(0, 10)
|
| strText = "Geben Sie eine Zahl zwischen 0 und 9 ein. "
|
| Console.Write(strText)
|
| intWahl = Console.ReadLine()
|
| If intZufall = intWahl Then
|
| Console.WriteLine("Sie haben einen Treffer gelandet.")
|
| Else
|
| If intZufall < intWahl Then
|
| Console.WriteLine("Ihre Zahl war zu gross.")
|
| ElseIf intZufall > intWahl Then
|
| Console.WriteLine("Ihre Zahl war zu klein.")
|
| End If
|
| Console.WriteLine("Die Lösung lautet: {0}", intZufall)
|
| End If
|
| Console.ReadLine()
|
| End Sub
|
| End Module
|
Die wesentlichste Neuerung ist das Ermitteln der Zufallszahl. Die .NET-Klassenbibliothek stellt zu diesem Zweck die Klasse Random zur Verfügung, aus der mit
ein Objekt erzeugt wird. Die weiteren Details brauchen uns an dieser Stelle nicht zu interessieren, sie sind Thema des Kapitels 4. Dieses Objekt ist nun unter dem Bezeichner rnd im Code anzusprechen. Ein Objekt des Typs Random veröffentlicht die Methode Next, die eine Ganzzahl aus einem Zahlenbereich ermittelt, der in den Klammern als Argument übergeben wird:
| <Random-Objekt>.Next(<Untergrenze, Obergrenze>)
|
Die Skala der zufällig ermittelten Zahl wird definitionsgemäß beschrieben durch
Untergrenze <= Zufallszahl < Obergrenze
Die Next-Methode liefert als Ergebnis ihres Aufrufs im obigen Beispiel eine Zahl, die der Variablen intZufall zugewiesen wird. Anschließend wird der Benutzer dazu aufgefordert, seinen Tipp abzugeben. Die äußere If-Anweisung prüft zunächst, ob der Tipp des Anwenders richtig war. Wenn nicht, verzweigt der Programmfluss in den Else-Zweig, der seinerseits wieder einen kompletten If-Block enthält, in dem geprüft wird, ob die Zufallszahl größer oder kleiner als der Tipp des Anwenders ist.
3.5.2 »Select-Case«-Anweisung  
Mit der If-Struktur können durchaus Bedingungen auf Basis verschiedener Vergleichsoperanden formuliert werden, die nicht in einem direkt erkennbaren Zusammenhang stehen. In der Praxis tritt jedoch häufig die Problematik der Untersuchung desselben Operanden auf.
Angenommen, eine Konsolenanwendung bietet dem Anwender eine Auswahl diverser Optionen an, mit welcher der weitere Ablauf des Programms vom Anwender beeinflusst werden kann.
| Option Compare Text
|
| Imports Microsoft.VisualBasic.ControlChars
|
| Module Module1
|
| Sub Main()
|
| Dim strMeldung, strWahl As String
|
| strMeldung = "Treffen Sie eine Wahl:" & crlf & crlf
|
| strMeldung &= "(N) – Neues Spiel" & crlf
|
| strMeldung &= "(A) – Altes Spiel fortsetzen" & crlf
|
| strMeldung &= "(E) – Beenden" & crlf
|
| Console.WriteLine(strMeldung)
|
| Console.Write("Ihre Eingabe: ")
|
| strWahl = Console.ReadLine()
|
| If strWahl = "N" Then
|
| 'Anweisungen, die ein neues Spiel starten
|
| ElseIf strWahl = "A" Then
|
| 'Anweisungen, um einen alten Spielstand zu laden
|
| ElseIf strWahl = "E" Then
|
| 'Anweisungen, um das Spiel zu beenden
|
| Else
|
| Console.Write("Falsche Eingabe.")
|
| 'Neueingabe ermöglichen
|
| End If
|
| Console.Read()
|
| End Sub
|
| End Module
|
In der ersten Zeile des Beispielcodes wird zunächst im Deklarationsteil des Moduls mit der Imports-Anweisung der Namespace Microsoft.VisualBasic.ControlChars bekannt gegeben. Im weiteren Verlauf dieses Buches werden wir uns die Namespaces natürlich noch genauer ansehen. In dem importierten Namensraum ist eine Stringkonstante definiert, die den Zeilenumbruch einer Zeichenfolge ermöglicht: crlf (Carriage Return – Line Feed). Die Konstante wird dazu benutzt, um unter Zuhilfenahme des &-Operators in der Stringvariablen strMeldung Zeilenumbrüche festzulegen.
Der Anwender wird dazu aufgefordert, sich für eine von drei angebotenen Optionen zu entscheiden. Seine Wahl – die Eingabe des Buchstabens, der vor jeder Option in Klammern angeführt ist – wird der Variablen strWahl zugewiesen. Im weiteren Verlauf findet in der If-Struktur nur noch die Prüfung dieser Variablen statt.
Alternativ dazu könnte die Verzweigungsstruktur auch mit einer Select Case-Anweisung realisiert werden. Aus dem obigen Beispiel müsste der gesamte Programmteil If...End If durch den folgenden ersetzt werden:
| ' ----------------------------------------------------------
|
| ' Beispiel: ...\Kapitel 3\SelectCase
|
| '-----------------------------------------------------------
|
| ...
|
| Select Case strWahl
|
| Case "N"
|
| 'Anweisungen, die ein neues Spiel starten
|
| Case "A"
|
| 'Anweisungen, um einen alten Spielstand zu laden
|
| Case "E"
|
| 'Anweisungen, um das Spiel zu beenden
|
| Case Else
|
| Console.Write("Falsche Eingabe. Versuchen Sie es noch einmal.")
|
| 'Neueingabe ermöglichen
|
| End Select
|
| ...
|
Hinter Select Case wird der Ausdruck angegeben, der überprüft werden soll. Im Beispiel ist es der Inhalt der Variablen strWahl. Für jedes in Frage kommende Ergebnis der Prüfung wird ein eigener Case-Zweig angegeben. Entspricht der Inhalt des Testausdrucks, also in unserem Fall der Inhalt der Variablen strWahl, dem der Angabe hinter Case, werden alle Anweisungen bis zum nächsten Case ausgeführt. Danach wird das Programm hinter dem blockschließenden End Select fortgesetzt. Die Case Else-Anweisungen werden genau dann ausgeführt, wenn kein zutreffender Ausdruck in den Case-Zweigen vorher gefunden wird.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 3.9 Flussdiagramm des Select Case-Statements
In einem Case-Zweig können auch mehrere Bedingungen formuliert werden. Dazu kann mit
jede Bedingung einzeln aufgeführt werden. Eine zweite, gleichwertige Alternative wäre die Definition eines Gültigkeitsbereichs mit:
Eine weitere Variante stellt die Formulierung der Bedingung mit Is im Zusammenspiel mit den Vergleichsoperatoren dar. So wird beispielsweise im folgenden Codefragment die Variable iVar im Case-Zweig dahingehend geprüft, ob ihr Inhalt kleiner 5 ist.
| Select Case iVar
|
| Case Is < 5
|
| 'Anweisungen
|
Ausgenommen sind nur die Vergleichsoperatoren Is und Like, die zur Formulierung der Bedingung nicht eingesetzt werden dürfen.
3.5.3 Einzeilige Entscheidungsanweisungen  
Neben den beiden zuvor behandelten bedingten Anweisungen If ... Then ... Else und Select Case gibt es in Visual Basic 2005 noch zwei weitere, mehr spezialisierte: die Funktionen Choose und IIf. Richtig eingesetzt bieten sie eine Ergänzung zu den Bedingungsanweisungen. Allerdings werden sie auch meist ein wenig vernachlässigt und sind nicht allzu häufig anzutreffen.
Die »Choose«-Funktion
Um die Charakteristik der Choose-Funktion zu verstehen, schauen wir uns zunächst die vereinfachte Syntax an:
| Choose(<Index>,<Wahl 1> [,<Wahl 2>,...,[<Wahl n>)]])
|
Choose gibt einen Wert aus einer Liste von Wahlmöglichkeiten zurück. Der erste Parameter enthält einen numerischen Ausdruck, über den der Funktion mitgeteilt wird, welcher der folgenden Auswahlparameter als Resultat des Aufrufs von der Funktion zurückgeliefert wird. Zum besseren Verständnis schauen wir uns das an einem Beispiel an:
| ' ----------------------------------------------------------
|
| ' Beispiel: ...\Kapitel 3\Choose_Funktion_1
|
| ' ----------------------------------------------------------
|
| Module Module1
|
| Sub Main()
|
| Dim strValue As Object
|
| Dim dblWahl As Double = 3
|
| strValue = Choose(dblWahl, "Aachen", "Rom", "Paris", "München")
|
| Console.WriteLine(strValue)
|
| Console.ReadLine()
|
| End Sub
|
| End Module
|
Der zurückgelieferte Wert basiert auf dem Inhalt des ersten Parameters Index. Hat Index (in unserem Beispiel mit dblWahl bezeichnet) den Wert 1, ist der Rückgabewert »Aachen«, hat der erste Parameter den Inhalt 2, wäre der Rückgabewert »Rom«, usw. Im Beispiel oben wird statisch die Zahl 3 übergeben, also lautet die Anzeige an der Konsole »Paris«.
Wenn Sie die Choose-Funktion einsetzen, müssen Sie folgende Regeln beachten:
|
Der Rückgabewert ist immer vom Datentyp Object. |
|
Der Parameter Index ist vom Typ Double, eventuelle Nachkommastellen werden abgeschnitten. (Anmerkung: Die Hilfe zu dieser Funktion sagt zwar an dieser Stelle etwas anderes aus, aber Irren ist ...) |
|
Wird eine Zahl übergeben, die entweder negativ ist oder die Anzahl der Listenelemente übersteigt, ist die Rückgabe leer. |
Die Choose-Funktion ist sehr flexibel einsetzbar. Sie können, wie oben gezeigt, ihr nicht nur einzelne Elemente übergeben, es kann auch ein Array sein. Wir wollen uns diese Variante anschauen und die Städtenamen des Beispiels in ein Feld schreiben, das an die Funktion übergeben wird.
| ' ----------------------------------------------------------
|
| ' Beispiel: ...\Kapitel 3\Choose_Funktion_2
|
| ' ----------------------------------------------------------
|
| Module Module1
|
| Sub Main()
|
| Dim strValue As Object
|
| Dim dblWahl As Double = 3
|
| Dim obj(3) As String
|
| obj(0) = "Aachen"
|
| obj(1) = "Rom"
|
| obj(2) = "Paris"
|
| obj(3) = "München"
|
| strValue = Choose(dblWahl, obj)
|
| Console.WriteLine(strValue)
|
| Console.ReadLine()
|
| End Sub
|
| End Module
|
Beachten Sie, dass zur Übergabe des Arrays nur der Bezeichner der Choose-Funktion mitgeteilt wird. Das funktioniert tadellos, weil ein Feld im Speicher durch die Aneinanderreihung typgleicher Daten beschrieben wird. Der Feldbezeichner ist dabei als ein Zeiger auf die Startadresse des Arrays zu verstehen. Das Ergebnis ist dasselbe wie im ersten Beispiel. Die Flexibilität dieser Variante ist allerdings deutlich höher, weil zur Laufzeit die Auswahlliste an die aktuellen Anforderungen angepasst werden kann.
Es spielt im Übrigen keine Rolle, von welchem Typ die aufgelisteten Elemente sind, weil Choose per Definition die Elemente auf den allgemeinen Typ Object zurückführt und es sich dabei um beliebige Zahlen, Strings oder auch konkrete Objekte handeln kann.
Die »IIf«-Funktion
Die IIf-Funktion kann nur unterscheiden, ob ein übergebener Ausdruck wahr oder falsch ist. Die Syntax der Funktion lautet:
| IIf(<Ausdruck>, <True-Parameter>, <False-Parameter>)
|
Ist Ausdruck wahr, wird True-Parameter ausgewertet, ansonsten False-Parameter. Auch das sei an einem Beispiel demonstriert.
| ' ----------------------------------------------------------
|
| ' Beispiel: ...\Kapitel 3\IIf_Funktion
|
| '-----------------------------------------------------------
|
| Option Compare Text
|
| Module Module1
|
| Sub Main()
|
| Dim strTrue = "1000 Euro werden überwiesen."
|
| Dim strFalse = "Dann bekommt das Geld die Tante."
|
| Dim bolTest As Boolean
|
| Dim objResult As Object
|
| 'den Benutzer zu einer Eingabe auffordern
|
| Console.Write("Wollen Sie etwas Geld erben (J/N)? ")
|
| 'die Eingabe entgegennehmen
|
| Dim strAnswer As String = Console.ReadLine()
|
| 'die Zulässigkeit der Benutzereingabe überprüfen, die nur
|
| 'J bzw. j oder N bzw. n lauten darf
|
| If strAnswer = "J" OrElse strAnswer = "N" Then
|
| If strAnswer = "J" Then bolTest = True
|
| Console.WriteLine(IIf(bolTest, strTrue, strFalse))
|
| Else
|
| Console.WriteLine("Falsche Eingabe.")
|
| End If
|
| Console.ReadLine()
|
| End Sub
|
| End Module
|
Der Anwender wird zur Laufzeit zu der Eingabe »J« oder »N« aufgefordert. Damit bei einem textuellen Vergleich die Unterscheidung zwischen Groß- und Kleinschreibung unberücksichtigt bleibt, wird Option Compare Text gesetzt. Nach der Deklaration der notwendigen Variablen und der Anzeige eines entsprechenden Textes wird die Eingabe des Benutzers mit
| Dim strAnswer As String = Console.ReadLine()
|
direkt zur Initialisierung der Variablen strAnswer benutzt. Im darauf folgenden Schritt wird zunächst überprüft, ob strAnswer eines der beiden zulässigen Zeichen enthält.
| If strAnswer = "J" OrElse strAnswer = "N" Then
|
Sinnvoll ist es, an dieser Stelle mit dem Operator OrElse die beiden Bedingungen zu prüfen. Hat der Anwender »J« oder »j« eingegeben (was als sehr wahrscheinlich angesichts der Fragestellung ist), kann auf die zweite Bedingungsprüfung verzichtet werden. Die Anweisung
| If strAnswer = "J" Then bolTest = True
|
weist der das IIf-Konstrukt steuernden Variablen bolTest True zu, wenn der Anwender auf das Geld nicht verzichten möchte. Da eine boolesche Variable automatisch False initialisiert wird, brauchen wir den zweiten und weniger wahrscheinlichen Fall nicht zu berücksichtigen.
Den Kern unseres Beispiels bildet die Codezeile:
| Console.WriteLine(IIf(bolTest, strTrue, strFalse))
|
Hat unser Anwender sich schweren Herzens dazu durchgerungen, das Geld als kleine Taschengelderhöhung zu akzeptieren, gilt: bolTest=True. Es wird daraufhin der Inhalt der Stringvariablen strTrue an der Konsole ausgegeben.
Der IIf-Funktion können Sie jeglichen Datentyp übergeben, einschließlich konkretisierter Objekte, da sowohl der True- als auch der False-Parameter vom Typ Object sind. Der Rückgabewert von IIf ist ebenfalls ein Object-Typ.
3.5.4 Zusammenfassung  
|
Zur Prüfung von Bedingungen bietet sich in den meisten Fällen die If-Anweisung an. Die zu prüfende Bedingung muss einen booleschen Wert zurückliefern. |
|
Mehrzeilige If-Anweisungen werden mit End If abgeschlossen, bei einzeiligen können Sie auf den Blockabschluss verzichten. |
|
Anstelle einer aufwändig strukturierten If-Anweisung kann, wenn immer derselbe Ausdruck geprüft werden soll, alternativ das Select Case-Statement benutzt werden. |
|
Mit der Choose- und IIf-Funktion werden weitere Kontrollstrukturen angeboten, die unter besonderen Umständen eine Alternative zur If-Anweisung darstellen. |
|